Análisis Exploratorio de Datos. La Maratón de Tokyo 2025.

Autores/as

Alba Martínez de la Hermosa

Alonso González Romero

Daniel López Paredes

Fecha de publicación

16 de octubre de 2025

Resumen
MODIFICAR.
Palabras clave

maratón, análisis de datos, Tokyo

1 Importación de librerías.

# Importación de todas las librerías usadas durante el informe y breve descripcion
library(readr) # Libreria para poder importar los datos desde un csv.
library(ggplot2) # Libreria para poder hacer gráficos.
library(DT) # Libreria para poder visualizar dataframes en qmd de manera interactiva.
library(hms) # Libreria para poder tratar datos referentes a horas, minutos y segundos.
library(dplyr) # Libreria para poder manipular dataframes.

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
library(ggplot2) # Libreria para poder hacer gráficos.
library(tidyr) # Libreria para poder transformar dataframes.
library(e1071) # para skewness() y kurtosis()

Attaching package: 'e1071'
The following object is masked from 'package:ggplot2':

    element

2 Introducción

El maratón de Tokyo 2025 se celebró, en su 18ª edición, el domingo 2 de marzo de 2025. Esta edición forma parte de los World Marathon Majors y abrió la temporada 2025 de los grandes maratones internacionales. El recorrido atraviesa distintos puntos icónicos de la ciudad de Tokyo, estando su inicio frente al edificio del Gobierno Metropolitano y la línea de meta cerca de la estación Tokyo/Gyoko-dori Avenue. El maratón de Tokyo es un evento de gran participación, tanto de atletas de élite como de aficionados.

En el presente documento se realizará un análisis exploratorio de datos (EDA) con el objetivo de extraer información relevante acerca de los resultados de los corredores que participaron en dicha prueba.

3 Presentación y Descripción del Dataset

Los resultados del maratón de Tokyo han sido extraídos a través del siguiente enlace. La extracción se ha realizado a través de técnicas de Web Scrapping con el objetivo de poder obtener datos relevantes referentes a los resultados de los corredores participantes.

3.1 Importación del Dataset

A continuación, se realiza la correspondiente importación de los datos extraídos:

resultadosTokyo2025 <- read_csv(
  "data/Maraton_Tokyo/marathon_tokyo_results_2025.csv",
  col_types = cols(
    BIB = col_integer(),
    Nombre = col_character(),
    Nacionalidad = col_character(),
    Genero = col_character(),
    Edad = col_integer(),
    tiempo_oficial = col_time(format = "%H:%M:%S"),
    parcial_5km = col_time(format = "%H:%M:%S"),
    parcial_10km = col_time(format = "%H:%M:%S"),
    parcial_15km = col_time(format = "%H:%M:%S"),
    parcial_20km = col_time(format = "%H:%M:%S"),
    medio_maraton = col_time(format = "%H:%M:%S"),
    parcial_25km = col_time(format = "%H:%M:%S"),
    parcial_30km = col_time(format = "%H:%M:%S"),
    parcial_35km = col_time(format = "%H:%M:%S"),
    parcial_40km = col_time(format = "%H:%M:%S")
  ),
  quote = "\""
)

# Transformacion a formato dataframe.
resultadosTokyo2025 <- as.data.frame(resultadosTokyo2025)

3.2 Descripción del Dataset

El dataframe importado, resultadosTokyo2025, consta de las siguientes variables:

Variable Tipo Descripción Unidades
BIB Numérica/Entero Número de dorsal asignado al corredor, valor único Número entero
Nombre Cadena de texto Nombre y apellidos del corredor Texto
Nacionalidad Cadena de texto País de procedencia del corredor Texto
Genero Categórica Género del corredor Texto
Edad Numérica/Entero Edad del corredor Años
tiempo_oficial Tiempo Tiempo total oficial de la maratón (gross time) hh:mm:ss
parcial_5km Tiempo Tiempo de paso en el km 5 hh:mm:ss
parcial_10km Tiempo Tiempo de paso en el km 10 hh:mm:ss
parcial_15km Tiempo Tiempo de paso en el km 15 hh:mm:ss
parcial_20km Tiempo Tiempo de paso en el km 20 hh:mm:ss
medio_Maraton Tiempo Tiempo al paso del medio maratón (21,097 km) hh:mm:ss
parcial_25km Tiempo Tiempo de paso en el km 25 hh:mm:ss
parcial_30km Tiempo Tiempo de paso en el km 30 hh:mm:ss
parcial_35km Tiempo Tiempo de paso en el km 35 hh:mm:ss
parcial_40km Tiempo Tiempo de paso en el km 40 hh:mm:ss

Nota: El gross time es el tiempo que tarda un corredor en terminar la maratón desde que se da el pistoletazo de salida, no desde que cruza la línea de inicio de la prueba

3.3 Lectura de una fila

Una vez que conocemos el significado de cada variable por separado, se va a proceder a la lectura de la primera fila del dataframe con el objetivo de mejorar la comprensión sobre el formato de los datos:

# Lo visualizamos con la libreria DT porque es más interactiva a la hora de generar el documento qmd.
datatable(
  resultadosTokyo2025,
  options = list(
    pageLength = 1, # cuántas filas mostrar
    scrollX = TRUE, # habilita scroll horizontal si la fila es muy ancha
    dom = 't' # solo muestra la tabla sin paginación ni búsqueda
  ),
  rownames = FALSE # quitar número de fila en la tabla
)
Warning in instance$preRenderHook(instance): It seems your data is too big for
client-side DataTables. You may consider server-side processing:
https://rstudio.github.io/DT/server.html

Se puede observar al corredor Tadese Takele, de nacionalidad Etíope, que corrió la maratón con el dorsal número 5. Tadese completó la maratón a sus 22 años con un tiempo de 2 horas, 3 minutos y 23 segundos, pudiendose observar sus tiempos de paso cada 5 kilómetros y en el punto de la media maratón.

3.4 Dimensiones del dataset

filas <- nrow(resultadosTokyo2025)
columnas <- ncol(resultadosTokyo2025)
cat(
  "El dataframe resultadosTokyo2025 contiene",
  filas,
  "filas y",
  columnas,
  "columnas."
)
El dataframe resultadosTokyo2025 contiene 36173 filas y 15 columnas.
# Elimino el número de filas y columnas con el objetivo de no sobrecargar el enviroment.
rm(filas, columnas)

3.5 Análisis Univariante

DF TEMPORAL PARA EMPEZAR SECCION

df_seconds <- resultadosTokyo2025 %>%
  mutate(
    across(
      matches("^tiempo_oficial$|^parcial_\\d+km$|^medio_maraton$"),
      ~ as.numeric(.x)
    )
  )

# Vista previa
glimpse(df_seconds)
Rows: 36,173
Columns: 15
$ BIB            <int> 5, 3, 4, 12, 13, 16, 1, 15, 2, 110, 8, 105, 112, 22, 11…
$ Nombre         <chr> "タデセ タケレ / TADESE TAKELE", "デレサ ゲレタ / DERESA GELETA", "…
$ Nacionalidad   <chr> "ETHIOPIA", "ETHIOPIA", "KENYA", "KENYA", "ETHIOPIA", "…
$ Genero         <chr> "男性/Men", "男性/Men", "男性/Men", "男性/Men", "男性/Men", "男性/M…
$ Edad           <int> 22, 29, 26, 26, 26, 25, 33, 26, 28, 28, 30, 32, 27, 26,…
$ tiempo_oficial <dbl> 7403, 7431, 7440, 7534, 7546, 7546, 7546, 7557, 7559, 7…
$ parcial_5km    <dbl> 865, 865, 865, 865, 880, 888, 865, 889, 881, 890, 865, …
$ parcial_10km   <dbl> 1736, 1735, 1736, 1736, 1763, 1780, 1735, 1782, 1763, 1…
$ parcial_15km   <dbl> 2610, 2610, 2610, 2611, 2650, 2671, 2610, 2672, 2651, 2…
$ parcial_20km   <dbl> 3487, 3487, 3487, 3488, 3535, 3568, 3487, 3570, 3537, 3…
$ medio_maraton  <dbl> 3678, 3678, 3682, 3679, 3728, 3763, 3678, 3764, 3729, 3…
$ parcial_25km   <dbl> 4364, 4364, 4363, 4365, 4416, 4456, 4364, 4458, 4417, 4…
$ parcial_30km   <dbl> 5242, 5243, 5242, 5255, 5316, 5352, 5254, 5353, 5316, 5…
$ parcial_35km   <dbl> 6133, 6133, 6133, 6193, 6225, 6264, 6193, 6264, 6225, 6…
$ parcial_40km   <dbl> 7019, 7030, 7036, 7127, 7145, 7166, 7146, 7166, 7149, 7…

Bajo este análisis se examinará cada variable del conjunto de datos para resumir su distribución, principales características y tendencias. Este tipo de análisis se centra en una sola variable a la vez, sin tener en cuenta su relación con otras, y permite obtener una primera descripción general de los datos. A continuación se revisa cada variable del dataset para observar sus principales características estadísticas.

3.5.1 Edad

Esta variable indica la edad de los corredores que participaron en la maratón. Primero se examinán los principales estadísticos de centralización: media, moda y mediana

# Fun. moda que devuelve el valor más frecuente (si hay empates devuelve el primero)
moda <- function(x) {
  x <- x[!is.na(x)]
  if (length(x) == 0) return(NA_real_)
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}

# Estadísticos resumidos (redondeados)
edad_stats <- df_seconds %>%
  summarise(
    n = sum(!is.na(Edad)),
    n_missing = sum(is.na(Edad)),
    media = round(mean(Edad, na.rm = TRUE), 2),
    mediana = median(Edad, na.rm = TRUE),
    moda = moda(Edad),
  )

datatable(
  edad_stats,
  options = list(dom = 't'),
  rownames = FALSE
)

Representado de forma gráfica, en la siguiente figura se observa un histograma con línea de densidad y los correspondientes valores de media, mediana y moda.

# Valores para líneas en los gráficos
mu <- edad_stats$media
med <- edad_stats$mediana
mod <- edad_stats$moda

p_hist <- ggplot(df_seconds, aes(x = Edad)) +
  geom_histogram(aes(y = ..density..), bins = 30, fill = "#90CAF9", color = "gray30") +
  geom_density(fill = "#1976D2", alpha = 0.15) +
  geom_vline(xintercept = mu, color = "#D32F2F", size = 1, linetype = "solid") +
  geom_vline(xintercept = med, color = "#F9A825", size = 1, linetype = "dashed") +
  geom_vline(xintercept = mod, color = "#2E7D32", size = 1, linetype = "dotdash") +
  annotate("text", x = mu, y = Inf, label = paste0("Media: ", mu), vjust = 2.2, color = "#D32F2F", size = 3.5) +
  annotate("text", x = med, y = Inf, label = paste0("Mediana: ", med), vjust = 3.8, color = "#F9A825", size = 3.5) +
  annotate("text", x = mod, y = Inf, label = paste0("Moda: ", mod), vjust = 5.4, color = "#2E7D32", size = 3.5) +
  labs(title = "Distribución de Edad (histograma + densidad)",
       x = "Edad (años)", y = "Densidad") +
  theme_minimal(base_size = 12)
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
p_hist
Warning: The dot-dot notation (`..density..`) was deprecated in ggplot2 3.4.0.
ℹ Please use `after_stat(density)` instead.

3.5.2 Género

3.5.3 Nacionalidad

3.5.4 Tiempo Oficial

3.5.5 Tiempos Parciales

3.6 Análisis Bivariante y Multivariante

3.7 Identificación de Patrones y Formulación de Preguntas

3.8 Conclusiones